﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;

using DAL;
using BLL;

namespace CTRL
{
    public sealed class CtrlBooking
    {
        private static volatile CtrlBooking instance;
        private static object syncRoot = new Object();

        private IDALBooking bookingRepo;

        private CtrlCar ctrlCar;
        private CtrlBatteryItem ctrlBatteryItem;
        private CtrlBookingItem ctrlBookingItem;

        public CtrlBooking()
        {
            this.bookingRepo = new DALBooking(new CarBatteryEntities());

            ctrlCar = CtrlCar.Instance;
            ctrlBatteryItem = CtrlBatteryItem.Instance;
            ctrlBookingItem = CtrlBookingItem.Instance;
        }

        public static CtrlBooking Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                            instance = new CtrlBooking();
                    }
                }

                return instance;
            }
        }

        public IEnumerable<Booking> GetAllBookings()
        {
            return bookingRepo.GetBookings();
        }

        public List<Booking> GetAllBookingsOfCustomer(int customerID)
        {
            List<Booking> bookingList = new List<Booking>();
            List<Car> customersCars = ctrlCar.GetAllCarsOwnedByCustomer(customerID).ToList();

            foreach (Car _car in customersCars)
            {
                List<Booking> bookingsByCar = bookingRepo.GetBookings().Where(x => x.carID.Equals(_car.carID)).ToList();

                foreach (Booking _book in bookingsByCar)
                    bookingList.Add(_book);
            }

            return bookingList;
        }

        public Booking GetBooking(int bookingID)
        {
            return bookingRepo.GetBookingByID(bookingID);
        }

        public int CreateBooking(int carID, List<BatteryCenterDistance> bcdList)
        {
            Car car = ctrlCar.GetCar(carID);
            Booking newBooking = new Booking() { bDate = DateTime.Now, carID = carID };

            bookingRepo.InsertBooking(newBooking);

            int result = bookingRepo.Save();

            try
            {
                Dictionary<BatteryCenter, BatteryItem> dictCenterItem = FindSuitableBatteries(bcdList, car, BatteryState.reserved);

                foreach (var entry in dictCenterItem)
                    ctrlBookingItem.AddBookingItem(entry.Value, newBooking.bookingID, entry.Key.stationID);

                return result;
            }
            catch (Exception)
            {
                bookingRepo.DeleteBooking(newBooking.bookingID);

                throw new Exception("The booking was not added");
            }
        }

        public int CancelBooking(int ID)
        {
            Booking book = GetBooking(ID);

            if (book != null)
            {
                bookingRepo.DeleteBooking(ID);
                ctrlBookingItem.DeleteBookingItemsFromBooking(ID);

                return bookingRepo.Save();
            }
            else
                throw new Exception("The Booking was not cancelled");
        }

        public Dictionary<BatteryCenter, BatteryItem> FindSuitableBatteries(List<BatteryCenterDistance> bcdList, Car _car, BatteryState state)
        {
            Dictionary<BatteryCenter, BatteryItem> dictStationBattery = new Dictionary<BatteryCenter, BatteryItem>();

            foreach (BatteryCenterDistance bcd in bcdList)
            {
                if (bcd.Stop)
                {
                    IEnumerable<BatteryItem> listBatteries = ctrlBatteryItem.FindBatteryItemsAtLocationState(bcd.BatteryCenter.name, _car.capacity, state);

                    if (listBatteries.Count() > 0)
                        dictStationBattery.Add(bcd.BatteryCenter, listBatteries.FirstOrDefault());
                }
            }

            return dictStationBattery;
        }

        public Dictionary<BatteryCenter, BatteryItem> ReserveBatteries(List<BatteryCenterDistance> listBCD, Car car)
        {
            Dictionary<BatteryCenter, BatteryItem> listReserve = FindSuitableBatteries(listBCD, car, BatteryState.available);

            ctrlBatteryItem.UpdateBatteryStates(listReserve.Select(x => x.Value).ToList(), BatteryState.reserved);

            return listReserve;
        }

        public void UnReserveBatteries(List<BatteryItem> listBatteryItem)
        {
            ctrlBatteryItem.UpdateBatteryStates(listBatteryItem, BatteryState.available);
        }

        public bool CanCancelBooking(int bookingID)
        {
            bool possible = true;
            var bookingItems = ctrlBookingItem.GetBookingItemsFromBooking(bookingID);

            foreach (BookingItem bookItem in bookingItems)
            {
                BatteryItem batItem = ctrlBatteryItem.GetBatteryItem(bookItem.batteryID);

                if (!batItem.state.Equals(BatteryState.booked.ToString()))
                    possible = false;
            }

            return possible;
        }
    }
}